#include "client.h"

int RecvFile(int peerFd, char* destPath){
    //接收标志位，判断发送端文件是否存在
    int flag = 0;
    int ret;
    ret = recv(peerFd, &flag, 4, MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    }
    if(0 == ret){
        printf("conn has closed.\n");
        close(peerFd);
        return -1;
    }
    //标志位为0文件不存在，直接返回
    if(flag == 0){
        printf("file is not existed.\n");
        return -1;
    }
    //定义应用层缓冲区和接收字节数，以及相关变量
    int length;
    char fileName[BUFFSIZE] = {0};
    off_t byteHaveRecv = 0;
    //接收文件名
    ret = recv(peerFd, &length, 4, MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    } else if(ret == 0) {
        printf("conn has closed.\n");
        close(peerFd);
        return -1;
    }
    memset(fileName, 0, BUFFSIZE);
    ret = recv(peerFd, fileName, length, MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    } else if(ret == 0) {
        close(peerFd);
        return -1;
    }
    printf("%s\n", fileName);
    //接收文件大小
    off_t fileSize;
    ret = recv(peerFd, &fileSize, sizeof(off_t), MSG_WAITALL);
    if(ret < 0){
        perror("recv");
        return -1;
    } else if(ret == 0) {
        close(peerFd);
        return -1;
    }
    char nameBuff[BUFFSIZE] = {0};
    //去掉末尾的'/'
    if(destPath[strlen(destPath) - 1] == '/'){
        destPath[strlen(destPath) - 1] = '\0';
    }
    //获得下载文件的目的地
    if(strlen(destPath) > 0){
        sprintf(nameBuff, "%s/%s", destPath, fileName);
        strcpy(fileName, nameBuff);
    }
    //在本地打开或者创建文件
    int fd = open(fileName, O_RDWR | O_CREAT, 0666);
    if(fd == -1) {
        perror("open");
        return -1;
    }
    //获取本地文件信息
    struct stat st;
    ret = fstat(fd, &st);
    if(ret < 0){
        perror("fstat");
        close(fd);
        return -1;
    }
    byteHaveRecv = st.st_size;
    if(byteHaveRecv != fileSize && byteHaveRecv > _10M){//断点传输时每次多传10M，防止异常导致文件传输出错
        byteHaveRecv -= _10M;
    }
    //发送已接收的文件长度
    Sendn(peerFd, &byteHaveRecv, sizeof(off_t));
    //使用零拷贝接收文件
    off_t byteNeedRecv = fileSize - byteHaveRecv;
    off_t byteNeedRecvNow = byteNeedRecv;
    off_t byteHaveRecvNow = byteHaveRecv;
    printf("fileSize: %ld\nbyteHaveRecv: %ld\nbyteNeedRecv: %ld\n", fileSize, byteHaveRecv, byteNeedRecv);
    printf("download percent: %5.2lf%%\n", (double)byteHaveRecvNow / fileSize * 100);
    //文件已存在，则不用接收
    if(byteNeedRecv == 0){ 
        printf("do not need recv.\n");
        close(fd);
        return 0;
    }
    //以10M为单位传输
    while(byteNeedRecvNow >= _10M) { 
        ftruncate(fd, byteHaveRecvNow + _10M);
        char* pbuf = (char*)mmap(NULL, _10M, PROT_READ | PROT_WRITE, MAP_SHARED, fd, byteHaveRecvNow);
        if(pbuf == MAP_FAILED) {
            perror("mmap");
            close(fd);
            return -1;
        }
        ret = recv(peerFd, pbuf, _10M, MSG_WAITALL);
        if(ret < 0){
            perror("recv");
            munmap(pbuf, _10M);
            close(fd);
            return -1;
        } else if(ret == 0) {
            munmap(pbuf, _10M);
            close(fd);
            close(peerFd);
            return -1;
        }
        byteNeedRecvNow -= _10M;
        byteHaveRecvNow += _10M;
        printf("download percent: %5.2lf%%\n", (double)byteHaveRecvNow / fileSize * 100);
        munmap(pbuf, _10M);
    }
    //将剩余不到10M的数据传输完成
    if(byteNeedRecvNow > 0){
        ftruncate(fd, byteHaveRecvNow + byteNeedRecvNow);
        char* pbuf = (char*)mmap(NULL, byteNeedRecvNow, PROT_READ | PROT_WRITE, MAP_SHARED, fd, byteHaveRecvNow);
        if(pbuf == MAP_FAILED) {
            perror("mmap");
            close(fd);
            return -1;
        }
        ret = recv(peerFd, pbuf, byteNeedRecvNow, MSG_WAITALL);
        if(ret < 0){
            perror("recv");
            munmap(pbuf, byteNeedRecvNow);
            close(fd);
            return -1;
        } else if(ret == 0) {
            munmap(pbuf, byteNeedRecvNow);
            close(fd);
            close(peerFd);
            return -1;
        }
        byteHaveRecvNow += byteNeedRecvNow;
        munmap(pbuf, byteNeedRecvNow);
    }
    printf("download percent: 100%%\n");
    close(fd);
    return 0;
}
